<?php
// +----------------------------------------------------------------------
// | ThinkPHP [ WE CAN DO IT JUST THINK IT ]
// +----------------------------------------------------------------------
// | Copyright (c) 2009 http://thinkphp.cn All rights reserved.
// +----------------------------------------------------------------------
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
// +----------------------------------------------------------------------
// | Author: liu21st <liu21st@gmail.com>
// +----------------------------------------------------------------------
// $Id$

/**
 +------------------------------------------------------------------------------
 * ThinkPHP 应用程序类 执行应用过程管理
 +------------------------------------------------------------------------------
 * @category   Think
 * @package  Think
 * @subpackage  Core
 * @author    liu21st <liu21st@gmail.com>
 * @version   $Id$
 +------------------------------------------------------------------------------
 */
class App
{//类定义开始

/**
 +----------------------------------------------------------
 * 应用程序初始化
 +----------------------------------------------------------
 * @access public
 +----------------------------------------------------------
 * @return void
 +----------------------------------------------------------
 */
static public function init()
{
	// 设定错误和异常处理
	set_error_handler(array('App','appError'));
	set_exception_handler(array('App','appException'));

	//cookie( 'LOGGED_USER', Null );
	//[RUNTIME]
	// 检查项目是否编译过
	// 在部署模式下会自动在第一次执行的时候编译项目
	if(defined('RUNTIME_MODEL')){
		// 运行模式无需载入项目编译缓存
	}elseif(is_file(RUNTIME_PATH.'/~app.php') && (!is_file(CONFIG_PATH.'config.php') || filemtime(RUNTIME_PATH.'/~app.php')>filemtime(CONFIG_PATH.'config.php'))) {
		// 直接读取编译后的项目文件
		C(include RUNTIME_PATH.'/~app.php');
	}else{
		// 预编译项目
		App::build();
	}
	//[/RUNTIME]

	$_REQUEST = array_merge($_POST,$_GET);

	if(C('SESSION_AUTO_START'))  session_start(); // Session初始化

	// 站点设置
	App::checkSiteOption();

	// 项目开始标签
	if(C('APP_PLUGIN_ON'))   tag('app_begin');

	// 设置系统时区 PHP5支
	if(function_exists('date_default_timezone_set'))
	date_default_timezone_set(C('DEFAULT_TIMEZONE'));

	// 允许注册AUTOLOAD方法
	if(C('APP_AUTOLOAD_REG') && function_exists('spl_autoload_register'))
	spl_autoload_register(array('Think', 'autoload'));

	// 应用调度过滤器：如果没有加载任何URL调度器,默认只支持 QUERY_STRING 方式
	// if(C('URL_DISPATCH_ON'))   Dispatcher::dispatch();


	// 储存GET/POST数据以备安全测试 【必须放在App::getModule()和App::getAction()前面】
	//if(C('SECURITY_TEST_ON'))   service('Security')->save();

	if(!defined('PHP_FILE'))	define('PHP_FILE',	_PHP_FILE_);	//当前执行文件的web访问路径
	if(!defined('MODULE_NAME')) define('MODULE_NAME',	App::getModule());	// Module名称
	if(!defined('ACTION_NAME')) define('ACTION_NAME',	App::getAction());	// Action操作

	// 加载模块配置文件
	if(is_file(CONFIG_PATH.strtolower(MODULE_NAME).'_config.php'))
	C(include CONFIG_PATH.strtolower(MODULE_NAME).'_config.php');

	App::checkUser();			// 用户认证
	App::checkLanguage();		// 语言检查
	App::checkTemplate();		// 模板检查

	if(C('HTML_CACHE_ON'))		// 开启静态缓存
	HtmlCache::readHTMLCache();

	// 项目初始化标签
	if(C('APP_PLUGIN_ON'))   tag('app_init');

	return ;
}
//[RUNTIME]
/**
 +----------------------------------------------------------
 * 读取配置信息 编译项目
 +----------------------------------------------------------
 * @access private
 +----------------------------------------------------------
 * @return string
 +----------------------------------------------------------
 */
static private function build()
{
	// 加载惯例配置文件 sociax:2010-1-13 修改
	C(include CORE_PATH.'/sociax/convention.php');

	// 加载平台配置文件 sociax:2010-1-15 修改
	if(is_file(SITE_PATH.'/config.inc.php'))
	C(include SITE_PATH.'/config.inc.php');

	// 加载路由配置文件
	if(is_file(SITE_PATH.'/data/config/router.inc.php'))
	C(include SITE_PATH.'/data/config/router.inc.php');

	// 加载访问控制配置文件
	if(is_file(SITE_PATH.'/data/config/access.inc.php'))
	C(include SITE_PATH.'/data/config/access.inc.php');

	// 加载项目配置文件
	if(is_file(CONFIG_PATH.'config.php'))
	C(include CONFIG_PATH.'config.php');

	$runtime = defined('RUNTIME_ALLINONE');
	$common   = '';
	//是否调试模式 ALL_IN_ONE模式下面调试模式无效
	$debug  =  C('APP_DEBUG') && !$runtime;
	// 加载项目公共文件
	if(is_file(COMMON_PATH.'common.php')) {
		include COMMON_PATH.'common.php';
		if(!$debug) // 编译文件
		$common   .= compile(COMMON_PATH.'common.php',$runtime);
	}
	// 加载项目编译文件列表
	if(is_file(CONFIG_PATH.'app.php')) {
		$list   =  include CONFIG_PATH.'app.php';
		foreach ($list as $file){
			// 加载并编译文件
			require $file;
			if(!$debug) $common   .= compile($file,$runtime);
		}
	}
	// 读取扩展配置文件
	$list = C('APP_CONFIG_LIST');
	foreach ($list as $val){
		if(is_file(CONFIG_PATH.$val.'.php'))
		C('_'.$val.'_',array_change_key_case(include CONFIG_PATH.$val.'.php'));
	}
	// 如果是调试模式加载调试模式配置文件
	if($debug) {
		// 加载系统默认的开发模式配置文件
		C(include THINK_PATH.'/Common/debug.php');
		if(is_file(CONFIG_PATH.'debug.php'))
		// 允许项目增加开发模式配置定义
		C(include CONFIG_PATH.'debug.php');
	}else{
		// 部署模式下面生成编译文件
		// 下次直接加载项目编译文件
		if(defined('RUNTIME_ALLINONE')) {
			// 获取用户自定义变量
			$defs = get_defined_constants(TRUE);

			//sociax:2010-1-12 修改核心，删除几个编译后被重复定义的常量。
			unset($defs['user']['SITE_DOMAIN'],$defs['user']['UPLOAD_PATH'],$defs['user']['SITE_PATH'],$defs['user']['CORE_PATH'],$defs['user']['APPS_PATH'],$defs['user']['ADDON_PATH'],$defs['user']['HAS_ONE'],$defs['user']['BELONGS_TO'],$defs['user']['HAS_MANY'],$defs['user']['MANY_TO_MANY'],$defs['user']['CLIENT_MULTI_RESULTS']);

			$content  = array_define($defs['user']);
			$content .= substr(file_get_contents(RUNTIME_PATH.'/~runtime.php'),5);
			$content .= $common."\nreturn ".var_export(C(),true).';';
			file_put_contents(RUNTIME_PATH.'/~allinone.php',strip_whitespace('<?php '.$content));
		}else{
			$content  = "<?php ".$common."\nreturn ".var_export(C(),true).";\n?>";
			file_put_contents(RUNTIME_PATH.'/~app.php',strip_whitespace($content));
		}
	}
	return ;
}
//[/RUNTIME]
/**
 +----------------------------------------------------------
 * 获得实际的模块名称
 +----------------------------------------------------------
 * @access private
 +----------------------------------------------------------
 * @return string
 +----------------------------------------------------------
 */
static private function getModule()
{
	$var  =  C('VAR_MODULE');
	$module = !empty($_POST[$var]) ?
	$_POST[$var] :
	(!empty($_GET[$var])? $_GET[$var]:C('DEFAULT_MODULE'));
	if(C('URL_CASE_INSENSITIVE')) {
		// URL地址不区分大小写
		define('P_MODULE_NAME',ucfirst($module));
		// 智能识别方式 index.php/user_type/index/ 识别到 UserTypeAction 模块
		$module = ucfirst(parse_name(P_MODULE_NAME,1));
	}
	unset($_POST[$var],$_GET[$var]);
	return $module;
}

/**
 +----------------------------------------------------------
 * 获得实际的操作名称
 +----------------------------------------------------------
 * @access private
 +----------------------------------------------------------
 * @return string
 +----------------------------------------------------------
 */
static private function getAction()
{
	$var  =  C('VAR_ACTION');
	$action   = !empty($_POST[$var]) ?
	$_POST[$var] :
	(!empty($_GET[$var])?$_GET[$var]:C('DEFAULT_ACTION'));
	unset($_POST[$var],$_GET[$var]);
	return $action;
}

/**
 +----------------------------------------------------------
 * 语言检查
 * 检查浏览器支持语言，并自动加载语言包
 +----------------------------------------------------------
 * @access private
 +----------------------------------------------------------
 * @return void
 +----------------------------------------------------------
 */
static private function checkLanguage()
{
	$langSet = C('DEFAULT_LANG');
	// 不开启语言包功能，仅仅加载框架语言文件直接返回
	if (!C('LANG_SWITCH_ON')){
		L(include THINK_PATH.'/Lang/'.$langSet.'.php');
		return;
	}
	// 启用了语言包功能
	// 根据是否启用自动侦测设置获取语言选择

	if (C('LANG_AUTO_DETECT')){
		if(isset($_GET[C('VAR_LANGUAGE')])){// 检测浏览器支持语言
			$langSet = $_GET[C('VAR_LANGUAGE')];// url中设置了语言变量
			cookie('think_language',$langSet,3600);
		}elseif(cookie('think_language')){// 获取上次用户的选择
			$langSet = cookie('think_language');
		}elseif(isset($_SERVER['HTTP_ACCEPT_LANGUAGE'])){// 自动侦测浏览器语言
			preg_match('/^([a-z\-]+)/i', $_SERVER['HTTP_ACCEPT_LANGUAGE'], $matches);
			$langSet = $matches[1];
			cookie('think_language',$langSet,3600);
		}
	}
	$langSet	=	strtolower($langSet);
	if($langSet!='zh-cn'){
		$langSet	=	'en';
	}

	// 定义当前语言
	define('LANG_SET',$langSet);
	if(C('LANG_AUTO_DETECT')){
		// 加载错误语言包
		if (is_file(LANG_PATH.$langSet.'/error.php'))
		L(include LANG_PATH.strtolower($langSet).'/error.php');

		// 加载全局语言包
		// Sam修改，不需要加载额外的包。只要加载common包即可
		//    dump(SITE_PATH.'/public/language/'.$langSet.'/common.php');
		if(is_file(SITE_PATH.'/public/language/'.$langSet.'/common.php')){
			L(include SITE_PATH.'/public/language/'.$langSet.'/common.php');
		}

		// 读取项目公共语言包
		if (is_file(LANG_PATH.$langSet.'/common.php'))
		L(include LANG_PATH.$langSet.'/common.php');

		// 读取当前模块语言包
		if (is_file(LANG_PATH.$langSet.'/'.$group.strtolower(MODULE_NAME).'.php'))
		L(include LANG_PATH.$langSet.'/'.$group.strtolower(MODULE_NAME).'.php');
	}
	// 加载框架语言包
	if(is_file(THINK_PATH.'/Lang/'.$langSet.'.php'))
	L(include THINK_PATH.'/Lang/'.$langSet.'.php');

}

/**
 +----------------------------------------------------------
 * 模板检查，如果不存在使用默认
 +----------------------------------------------------------
 * @access private
 +----------------------------------------------------------
 * @return void
 +----------------------------------------------------------
 */
static private function checkTemplate()
{
	//删除TP原模板目录切换功能
	$templateSet =    C('DEFAULT_THEME');
	//模版名称
	define('TEMPLATE_NAME',$templateSet);
	// 当前模版路径
	define('TEMPLATE_PATH',TMPL_PATH.TEMPLATE_NAME);
	$tmplDir = TMPL_DIR.'/'.TEMPLATE_NAME.'/';

	//当前项目地址
	define('__APP__',PHP_FILE.'?app='.APP_NAME);
	define('__URL__',PHP_FILE.'?app='.APP_NAME.'&mod='.MODULE_NAME);
	//当前页面地址
	define('__SELF__',$_SERVER['PHP_SELF']);
	// 应用URL根目录
	//if(C('APP_DOMAIN_DEPLOY')) {
	// 独立域名部署需要指定模板从根目录开始
	// $appRoot   =  '/';
	//}else{
	// sociax:2010-1-20 修改 ，增加apps目录
	$appRoot   =  __ROOT__.'/apps/'.APP_NAME.'/';
	//}

	C('TMPL_FILE_NAME', TEMPLATE_PATH.'/'.MODULE_NAME.'/'.ACTION_NAME.C('TMPL_TEMPLATE_SUFFIX'));
	C('CACHE_PATH',CACHE_PATH);

	//当前操作地址
	define('__ACTION__',__URL__);
	define('__CURRENT__', $appRoot.$tmplDir.MODULE_NAME);
	//项目模板目录
	define('APP_TMPL_PATH', $appRoot.$tmplDir);
	//网站公共文件目录
	define('WEB_PUBLIC_PATH', __ROOT__.'/public');
	//项目公共文件目录
	define('APP_PUBLIC_PATH', APP_TMPL_PATH.'Public');
	global $_OPT;

	$template	=	($_OPT['template'])?$_OPT['template']:'course';
	define('THEME_PATH'	,	SITE_PATH."/public/themes/{$template}");
	define('THEME_URL'	,	WEB_PUBLIC_PATH."/themes/{$template}");
	define('__THEME__'	,	WEB_PUBLIC_PATH."/themes/{$template}");
	//		dump(__SELF__);
	//		dump(APP_TMPL_PATH);
	//		dump(SITE_PATH);
	//		dump(WEB_PUBLIC_PATH);
	return ;
}

/**
 +----------------------------------------------------------
 * 模板站点配置
 +----------------------------------------------------------
 * @access private
 +----------------------------------------------------------
 * @return void
 +----------------------------------------------------------
 */
static private function checkSiteOption(){
	global $_OPT;
	//初始化站点配置信息，在站点配置中：表情，网站头信息，网站的应用列表，应用权限等
	$f_path	=	SITE_PATH.'/_runtime/_cache/';
	if(file_exists($f_path.'_option_site.php')){
		$_OPT['site']	=	F('_option_site','',$f_path);
	}else{
		$_OPT['site']	=	model('Xdata')->lget('site');
		F('_option_site',$_OPT['site'],$f_path);
	}
	return;
}

/**
 +----------------------------------------------------------
 * 模板站点配置
 +----------------------------------------------------------
 * @access private
 +----------------------------------------------------------
 * @return void
 +----------------------------------------------------------
 */
static private function checkUser(){
	//检查用户是否登录
	$passport	=	service('Passport');
	$isLogged	=	$passport->isLogged();

	//验证登陆
	if(!$isLogged){

		//未登录 - 给游客赋予ID
		if($_GET['invitecode']){  //邀请码保存到SESSION,暂只用于激活帐号用
			$_SESSION['invitecode'] = h($_GET['invitecode']);
		}

		//记录输入地址,登陆后返回指定页
		if( APP_NAME.'/'.MODULE_NAME != 'home/Public' && APP_NAME.'/'.MODULE_NAME != 'home/Index'){
			$_SESSION['refer_url'] = "http://".$_SERVER["HTTP_HOST"].$_SERVER["REQUEST_URI"];
		}

		//游客访问控制
		$acl	=	C('access');
		if(($acl[APP_NAME.'/'.MODULE_NAME.'/'.ACTION_NAME]===true || $acl[APP_NAME.'/'.MODULE_NAME.'/*']===true || $acl[APP_NAME.'/*/*']===true)){
		}else{
			U('home/Public/login','',true);
		}

	}else{
		//已登录 - 检查用户禁止 - 检查用户访问应用的权限

		//初始化用户资料
		if( $_SESSION['NEED_ACTIVE']==1 && APP_NAME.'/'.MODULE_NAME != 'home/Public'){
			redirect(U('home/Public/init',array('invitecode'=>$_SESSION['invitecode'])));
		}
	}

	//获取appid

	//检查app的各种权限

	return;
}

/**
 +----------------------------------------------------------
 * 执行应用程序
 +----------------------------------------------------------
 * @access public
 +----------------------------------------------------------
 * @return void
 +----------------------------------------------------------
 * @throws ThinkExecption
 +----------------------------------------------------------
 */
static public function exec()
{
	// 是否开启标签扩展
	$tagOn   =  C('APP_PLUGIN_ON');
	// 项目运行标签
	if($tagOn)  tag('app_run');

	//创建Action控制器实例
	$module  =  A(MODULE_NAME);
	//        if(!$module) {
	//			// 是否存在扩展模块
	//            $_module = C('_modules_.'.MODULE_NAME);
	//            if($_module) {
	//                // 'module'=>array('classImportPath'[,'className'])
	//                import($_module[0]);
	//                $class = isset($_module[1])?$_module[1]:MODULE_NAME.'Action';
	//                $module = new $class;
	//            }else{
	//                // 是否定义Empty模块
	//                $module = A("Empty");
	//            }
	//            if(!$module)
	//                // 模块不存在 抛出异常
	//                throw_exception(L('_MODULE_NOT_EXIST_').MODULE_NAME);
	//        }

	//获取当前操作名
	$action = ACTION_NAME;
	//执行当前操作
	call_user_func(array(&$module,$action));
	// 项目结束标签
	if($tagOn)  tag('app_end');
	return ;
}

/**
 +----------------------------------------------------------
 * 运行应用实例 入口文件使用的快捷方法
 +----------------------------------------------------------
 * @access public
 +----------------------------------------------------------
 * @return void
 +----------------------------------------------------------
 */
static public function run() {
	App::init();
	// 记录应用初始化时间
	if(C('SHOW_RUN_TIME'))  $GLOBALS['_initTime'] = microtime(TRUE);
	App::exec();
	// 保存日志记录
	if(C('LOG_RECORD')){
		Log::save();
	}
	return ;
}

/**
 +----------------------------------------------------------
 * 自定义异常处理
 +----------------------------------------------------------
 * @access public
 +----------------------------------------------------------
 * @param mixed $e 异常对象
 +----------------------------------------------------------
 */
static public function appException($e)
{
	halt($e->__toString());
}

/**
 +----------------------------------------------------------
 * 自定义错误处理
 +----------------------------------------------------------
 * @access public
 +----------------------------------------------------------
 * @param int $errno 错误类型
 * @param string $errstr 错误信息
 * @param string $errfile 错误文件
 * @param int $errline 错误行数
 +----------------------------------------------------------
 * @return void
 +----------------------------------------------------------
 */
static public function appError($errno, $errstr, $errfile, $errline)
{
	switch ($errno) {
		case E_ERROR:
		case E_USER_ERROR:
			$errorStr = "[$errno] $errstr ".basename($errfile)." 第 $errline 行.";
			if(C('LOG_RECORD')) Log::write($errorStr,Log::ERR);
			halt($errorStr);
			break;
		case E_STRICT:
		case E_USER_WARNING:
		case E_USER_NOTICE:
		default:
			$errorStr = "[$errno] $errstr ".basename($errfile)." 第 $errline 行.";
			Log::record($errorStr,Log::NOTICE);
			break;
	}
}

};//类定义结束
?>